package main

import (
	"github.com/go-redis/redis/v8"
	"strconv"
	"strings"
	"time"
)

type ICache interface {
	Get(key string) (string, error)
	MGet(keys []string) []string
	Set(key string, value string)
	Del(key string)
	Clear()
	Keys(pattern string) []string
}

type RedisCache struct {
	client *redis.Client
}

func (c *RedisCache) MGet(keys []string) []string {
	var result []string
	val := c.client.MGet(ctx, keys...).Val()
	for _, v := range val {
		result = append(result, v.(string))
	}
	return result
}

func (c *RedisCache) Keys(pattern string) []string {
	return c.client.Keys(ctx, pattern).Val()
}

func NewRedisCache(host string, port int, password string) ICache {
	return &RedisCache{
		client: redis.NewClient(&redis.Options{
			Addr:     host + ":" + strconv.Itoa(port),
			Password: password,
			DB:       0,
		}),
	}
}

func (c *RedisCache) Get(key string) (string, error) {
	return c.client.Get(ctx, key).Result()
}

func (c *RedisCache) Set(key string, value string) {
	c.client.Set(ctx, key, value, 5*time.Hour)
}

func (c *RedisCache) Del(key string) {
	c.client.Del(ctx, key)
}

func (c *RedisCache) Clear() {
	c.client.FlushDB(ctx)
}

type MemoryCache struct {
	cache map[string]string
	queue chan []string
}

func (c *MemoryCache) MGet(keys []string) []string {
	var result []string
	for _, key := range keys {
		result = append(result, c.cache[key])
	}
	return result
}

func (c *MemoryCache) Keys(pattern string) []string {
	var keys []string
	for key := range c.cache {
		if strings.Contains(key, pattern) {
			keys = append(keys, key)
		}
	}
	return keys
}

func NewMemoryCache() ICache {
	ins := &MemoryCache{
		cache: make(map[string]string),
		queue: make(chan []string),
	}
	//	 Concurrency safe set
	go func() {
		for i := range ins.queue {
			ins.cache[i[0]] = i[1]
		}
	}()
	return ins
}

func (c *MemoryCache) Get(key string) (string, error) {
	return c.cache[key], nil
}

func (c *MemoryCache) Set(key string, value string) {
	//	 Concurrency safe set
	c.queue <- []string{key, value}
}

func (c *MemoryCache) Del(key string) {
	delete(c.cache, key)
}

func (c *MemoryCache) Clear() {
	c.cache = make(map[string]string)
}
